package com.xb;



import com.xb.util.dbUtil;

import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * 抽象类用来实现增删改查的方法
 */
public abstract class BaseDao<T> {
    private Class<T> classz = null;

    /*
     * this.class是他的子类 因为是谁调用这个就是this的是谁 所以对应的是他的子类的this
     *返回Type，表示由这个class表示的实体(类、接口、基本类型或void)的直接超类。 com.xb.BaseDao<com.xb.Entity.Customers>
     *ParameterizedType表示一个参数化类型，例如Collection<String>。
     * 参数化类型在反射方法第一次需要时创建，如此包中指定的那样。在创建参数化类型p时，将解析p实例化的泛型类型声明，并递归创建p的所有类型参数。
     * 有关类型变量创建过程的详细信息，请参阅TypeVariable。重复创建参数化类型不会产生任何影响。
     */
    {
        Type genericSuperclass = this.getClass().getGenericSuperclass();
        ParameterizedType parameterizedType = (ParameterizedType) genericSuperclass;
        Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();//获取父类的泛型参数
        classz = (Class<T>) actualTypeArguments[0];
    }
    //查询
    public List<T> selectGetDataBytrasaction(Connection connection, String sql, Object... args) {
        PreparedStatement ps = null;
        ResultSet resultSet = null;
        T t = null;
        List<T> list = new ArrayList<>();
        try {
            ps = connection.prepareStatement(sql);
            if (args != null){
                for (int i = 0; i < args.length; i++) {
                    ps.setObject(i + 1, args[i]);
                }
            }
            resultSet = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取总列数
            int columnCount = metaData.getColumnCount();
            while (resultSet.next()) {
                t = classz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    //getCatalogName列名
                    /// 1. 获取列的别名
                    String catalogName = metaData.getColumnLabel(i + 1);
                    // 2. 根据列名获取对应数据表中的数据
                    Object object = resultSet.getObject(catalogName);
                    // 3. 将数据表中得到的数据，封装进对象
                    Field declaredField = classz.getDeclaredField(catalogName);
                    declaredField.setAccessible(true);
                    declaredField.set(t, object);
                }
                list.add(t);
            }
            return list;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            dbUtil.closeConAndPsAndRs(connection, ps,resultSet);
        }
        return null;
    }

    //创建一个通用的增删改的sql语句
    public  void DclSql(Connection connection, String sql, Object... args) {
        PreparedStatement ps = null;
        try {
            ps = connection.prepareStatement(sql);
            int len = args.length;
            for (int i = 1; i <= len; i++) {
                ps.setObject(i, args[i - 1]);
            }
            ps.execute();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            dbUtil.closeConAndPs(connection, ps);
        }
    }

    //查询单个的对象
    public  T selectInfo(Connection connection, String sql, Object... args) {
        T t = null;
        PreparedStatement ps = null;
        ResultSet  resultSet = null;
        try {
            ps = connection.prepareStatement(sql);
            for (int i = 0; i < args.length; i++) {
                ps.setObject(i + 1, args[i]);
            }
             resultSet = ps.executeQuery();
            //获取结果集的元数据
            ResultSetMetaData metaData = resultSet.getMetaData();
            //获取总列数
            int columnCount = metaData.getColumnCount();
            while (resultSet.next()) {
                t = classz.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    //getCatalogName列名
                    /// 1. 获取列的别名
                    String catalogName = metaData.getColumnLabel(i + 1);
                    // 2. 根据列名获取对应数据表中的数据
                    Object object = resultSet.getObject(catalogName);
                    // 3. 将数据表中得到的数据，封装进对象
                    Field declaredField = classz.getDeclaredField(catalogName);
                    declaredField.setAccessible(true);
                    declaredField.set(t, object);
                }
            }
            return t;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            dbUtil.closeConAndPsAndRs(null, ps,resultSet);
        }
        return null;
    }

    //查询公共的 函数什么的
    public  <E> E selectByFunction(Connection connection, String sql, Object... param) {
        PreparedStatement preparedStatement = null;
        ResultSet resultSet = null;
        try {
            preparedStatement = connection.prepareStatement(sql);
               if (param !=null){
                   for (int i = 0; i < param.length; i++) {
                       preparedStatement.setObject(i+1,param[i]);
                   }
               }
             resultSet = preparedStatement.executeQuery();
            if (resultSet.next()){
                return (E)resultSet.getObject(1);
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            dbUtil.closeConAndPsAndRs(null,preparedStatement,resultSet);
        }
        return null;
    }

    //获取连接的方法
    public static Connection getConnection() {
        Connection connection = null;
        try {
            InputStream resourceAsStream = ClassLoader.getSystemClassLoader().getResourceAsStream("mysqlConnectInfo.properties");
            Properties properties = new Properties();
            properties.load(resourceAsStream);
            String driver = properties.getProperty("driver");
            String user = properties.getProperty("user");
            String password = properties.getProperty("password");
            String url = properties.getProperty("url");
            //通过反射加载驱动
            Class.forName(driver);
            connection = DriverManager.getConnection(url, user, password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return connection;
    }
}
